import re
import os
import tkinter as tk
from tkinter import filedialog, scrolledtext, messagebox, ttk
from functools import partial


class LatexPunctuationFixer:
    def __init__(self):
        # 定义中英文标点符号映射
        self.cn_to_en = {
            '，': ',', '。': '.', '；': ';', '：': ':', '"': '"', '"': '"',
            ''': "'", ''': "'", '【': '[', '】': ']', '（': '(', '）': ')',
            '？': '?', '！': '!', '、': ',', '《': '<', '》': '>'
        }
        self.en_to_cn = {v: k for k, v in self.cn_to_en.items()}
        
        # 特殊情况处理
        self.special_cases = {
            "e.g.": True,  # 英文缩写不替换
            "i.e.": True,
            "etc.": True,
            "et al.": True,
            "vs.": True,
            "Fig.": True,  # 图表引用不替换
            "Eq.": True,
            "Ref.": True,
        }
        
        # 编译正则表达式用于识别LaTeX结构
        self.re_latex_commands = re.compile(r'\\[a-zA-Z]+(\{[^{}]*\})*')
        self.re_latex_environments = re.compile(r'\\begin\{[^{}]*\}.*?\\end\{[^{}]*\}', re.DOTALL)
        self.re_latex_math = re.compile(r'\$\$.*?\$\$|\$.*?\$|\\begin\{equation\}.*?\\end\{equation\}', re.DOTALL)
        self.re_latex_comments = re.compile(r'%.*?$', re.MULTILINE)
        
        # 识别中英文的正则表达式
        self.re_chinese = re.compile(r'[\u4e00-\u9fff]+')
        self.re_english = re.compile(r'[a-zA-Z]+')

    def is_chinese_context(self, text, pos):
        """判断位置pos是否处于中文环境"""
        # 查找pos前后的字符
        before = text[:pos].strip()
        after = text[pos:].strip()
        
        # 如果前后都有中文，那么处于中文环境
        if before and after:
            has_cn_before = bool(self.re_chinese.search(before[-1:]))
            has_cn_after = bool(self.re_chinese.search(after[:1]))
            if has_cn_before and has_cn_after:
                return True
                
        # 查找最近的非空白字符
        i = pos - 1
        while i >= 0 and text[i].isspace():
            i -= 1
        
        if i >= 0:
            # 查找前面30个字符，判断中文字符占比
            context_before = text[max(0, i-30):i+1]
            cn_count = len(self.re_chinese.findall(''.join(context_before)))
            en_count = len(self.re_english.findall(''.join(context_before)))
            
            if cn_count > en_count:
                return True
        
        return False

    def is_in_latex_structure(self, text, pos):
        """检查位置pos是否在LaTeX命令或环境内"""
        # 所有可能的LaTeX结构
        structures = [
            self.re_latex_commands,
            self.re_latex_environments,
            self.re_latex_math,
            self.re_latex_comments
        ]
        
        for regex in structures:
            for match in regex.finditer(text):
                if match.start() <= pos < match.end():
                    return True
        
        return False
    
    def is_special_case(self, text, pos):
        """检查位置pos是否处于特殊情况（如英文缩写）"""
        for case in self.special_cases:
            case_len = len(case)
            if pos >= case_len - 1 and text[pos-case_len+1:pos+1] == case:
                return True
        return False

    def fix_punctuation(self, text):
        """修复文本中的标点符号，根据上下文环境智能替换"""
        # 先标记所有LaTeX结构，避免处理它们
        latex_positions = set()
        structures = [
            self.re_latex_commands,
            self.re_latex_environments,
            self.re_latex_math,
            self.re_latex_comments
        ]
        
        for regex in structures:
            for match in regex.finditer(text):
                for i in range(match.start(), match.end()):
                    latex_positions.add(i)
        
        # 将文本转换为字符列表，方便修改
        chars = list(text)
        
        # 逐个字符检查
        i = 0
        while i < len(chars):
            if i in latex_positions:
                i += 1
                continue
            
            # 判断当前字符是否是需要替换的标点
            if chars[i] in self.cn_to_en or chars[i] in self.en_to_cn:
                # 检查是否是特殊情况
                if self.is_special_case(text, i):
                    i += 1
                    continue
                
                # 判断上下文环境
                in_chinese = self.is_chinese_context(text, i)
                
                # 根据环境替换标点
                if in_chinese and chars[i] in self.en_to_cn:
                    chars[i] = self.en_to_cn[chars[i]]
                elif not in_chinese and chars[i] in self.cn_to_en:
                    chars[i] = self.cn_to_en[chars[i]]
            
            i += 1
        
        return ''.join(chars)


class LatexPunctuationFixerUI:
    def __init__(self, root):
        self.root = root
        self.root.title("LaTeX标点符号修正工具")
        self.root.geometry("900x700")
        
        self.fixer = LatexPunctuationFixer()
        
        # 设置UI
        self.create_ui()
    
    def create_ui(self):
        # 分割UI为上下两部分
        main_frame = ttk.Frame(self.root, padding="10")
        main_frame.pack(fill=tk.BOTH, expand=True)
        
        # 上部分：输入区域
        input_frame = ttk.LabelFrame(main_frame, text="输入LaTeX文本", padding="10")
        input_frame.pack(fill=tk.BOTH, expand=True, pady=(0, 5))
        
        self.input_text = scrolledtext.ScrolledText(input_frame, wrap=tk.WORD, width=80, height=15)
        self.input_text.pack(fill=tk.BOTH, expand=True)
        
        # 中间：按钮区域
        button_frame = ttk.Frame(main_frame)
        button_frame.pack(fill=tk.X, pady=5)
        
        ttk.Button(button_frame, text="打开TEX文件", command=self.open_file).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="修正标点符号", command=self.fix_punctuation).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="保存结果", command=self.save_file).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="覆盖原文件", command=self.overwrite_file).pack(side=tk.LEFT, padx=5)
        
        # 下部分：输出区域
        output_frame = ttk.LabelFrame(main_frame, text="修正后的文本", padding="10")
        output_frame.pack(fill=tk.BOTH, expand=True, pady=(5, 0))
        
        self.output_text = scrolledtext.ScrolledText(output_frame, wrap=tk.WORD, width=80, height=15)
        self.output_text.pack(fill=tk.BOTH, expand=True)
        
        # 状态栏
        self.status_var = tk.StringVar()
        self.status_var.set("就绪")
        status_bar = ttk.Label(self.root, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W)
        status_bar.pack(side=tk.BOTTOM, fill=tk.X)
        
        self.current_file = None

    def open_file(self):
        file_path = filedialog.askopenfilename(
            filetypes=[("TeX Files", "*.tex"), ("All Files", "*.*")]
        )
        if file_path:
            try:
                with open(file_path, 'r', encoding='utf-8') as file:
                    content = file.read()
                self.input_text.delete(1.0, tk.END)
                self.input_text.insert(tk.END, content)
                self.current_file = file_path
                self.status_var.set(f"已打开文件: {os.path.basename(file_path)}")
            except Exception as e:
                messagebox.showerror("错误", f"无法打开文件: {str(e)}")
                self.status_var.set("打开文件失败")

    def fix_punctuation(self):
        input_text = self.input_text.get(1.0, tk.END)
        if not input_text.strip():
            messagebox.showinfo("提示", "请先输入LaTeX文本或打开TEX文件")
            return
        
        try:
            fixed_text = self.fixer.fix_punctuation(input_text)
            self.output_text.delete(1.0, tk.END)
            self.output_text.insert(tk.END, fixed_text)
            self.status_var.set("标点符号修正完成")
        except Exception as e:
            messagebox.showerror("错误", f"处理文本时出错: {str(e)}")
            self.status_var.set("处理失败")

    def save_file(self):
        output_text = self.output_text.get(1.0, tk.END)
        if not output_text.strip():
            messagebox.showinfo("提示", "没有可保存的内容")
            return
        
        file_path = filedialog.asksaveasfilename(
            defaultextension=".tex",
            filetypes=[("TeX Files", "*.tex"), ("All Files", "*.*")]
        )
        if file_path:
            try:
                with open(file_path, 'w', encoding='utf-8') as file:
                    file.write(output_text)
                self.status_var.set(f"文件已保存至: {os.path.basename(file_path)}")
            except Exception as e:
                messagebox.showerror("错误", f"保存文件失败: {str(e)}")
                self.status_var.set("保存失败")

    def overwrite_file(self):
        if not self.current_file:
            messagebox.showinfo("提示", "没有原始文件可覆盖，请先打开文件")
            return
        
        output_text = self.output_text.get(1.0, tk.END)
        if not output_text.strip():
            messagebox.showinfo("提示", "没有可保存的内容")
            return
        
        confirm = messagebox.askyesno("确认", f"确定要覆盖原文件 {os.path.basename(self.current_file)} 吗？")
        if confirm:
            try:
                with open(self.current_file, 'w', encoding='utf-8') as file:
                    file.write(output_text)
                self.status_var.set(f"原文件已更新: {os.path.basename(self.current_file)}")
            except Exception as e:
                messagebox.showerror("错误", f"覆盖文件失败: {str(e)}")
                self.status_var.set("覆盖失败")


if __name__ == "__main__":
    root = tk.Tk()
    app = LatexPunctuationFixerUI(root)
    root.mainloop()
